#include<stdio.h>
#include<time.h>
#include<Windows.h>
#include<string.h>
#include<stdlib.h>
#include<conio.h>
#define qingping system("cls")  
typedef struct spot {
	int x;
	int y;
	struct spot* next;
}body;
body * tail = NULL;
body * head = NULL;
int direct = 4;   //1为上，2为左，3为下，4为右。
int speed = 100;
int foodx;
int foody;
int enforcespeed = 0;
int score_now = 0;
int wrong = 0;
int restart = 0;
int highscore=0;
FILE* fp = NULL;
void printsnake();
void welcometogame();
void createMap();
void ingame();
void score(int score_now);
void File_out();
void initsnake();
void createfood();
void snakemove();
void keyboardControl();
void lose();
void explanation();
void gotoxy(int x, int y);

int main() {
	srand((unsigned int)time(NULL));
	fp = fopen("highscore.txt", "r");
	if (fp) {
		fscanf(fp, "%d", &highscore);
		fclose(fp);
	}
	welcometogame();
	return 0;
}

void printsnake() {
	printf("   _________         _________\n");
	printf("  /         \\       /         \\\n");
	printf(" /  /~~~~~\\  \\     /  /~~~~~\\  \\\n");
	printf(" |  |     |  |     |  |     |  |\n");
	printf(" |  |     |  |     |  |     |  |\n");
	printf(" |  |     |  |     |  |     |  |        /\n");
	printf(" |  |     |  |     |  |     |  |      //\n");
	printf("(o  o)    \\  \\_____/  /     \\  \\_____//\n");
	printf(" \\__/      \\         /       \\       /\n");
	printf("  |         ~~~~~~~~~         ~~~~~~~\n");
	printf("  ^\n");
}

void welcometogame() {
	qingping;
	printsnake();
	printf("\n1.进入游戏\t2.观看说明\t3.退出游戏\n");
	while (1) {
		if (GetAsyncKeyState(49) & 0x8000) {
			createMap();
			initsnake();
			createfood();
			ingame();
		}
		else if (GetAsyncKeyState(50) & 0x8000) {
			explanation();
			qingping;
			printsnake();
			printf("\n1.进入游戏\t2.观看说明\t3.退出游戏\n");
		}
		else if (GetAsyncKeyState(51) & 0x8000) {
			break;
		};
		if (wrong != 0 && restart == 0) {
			qingping;
			break;
		}
		else if (wrong != 0) {
			createMap();
			wrong = 0;
			restart = 0;
			direct = 4;
			enforcespeed = 0;
			initsnake();
			createfood();
			ingame();
		}
	}
}

void createMap() {
	qingping ;
	for (int i = 0; i < 30; i++) {
		for (int j = 0; j < 30; j++) {
			if (i == 0 || i == 29 || j == 0 || j == 29) {
				printf("■ ");
			}
			else {
				printf("  ");
			}
		}
		if (i < 29) { printf("\n"); }
	}
	gotoxy(64, 8);
	printf("当前得分：0");
	gotoxy(64, 9);
	printf("历史最高分：%d",highscore);
	gotoxy(64, 18);
	printf("使用WASD/↑←↓→操控方向");
	gotoxy(64, 19);
	printf("F1/F2控制加速/减速");
	gotoxy(64, 20);
	printf("ESC自动失败");
}

void ingame() {
	while (1) {
		keyboardControl();
		snakemove();
		Sleep(speed);
		if (wrong !=0) {
			break;
		}

	}
}

void score(int score_now) {
	gotoxy(74, 8);
	printf("           ");
	gotoxy(74, 8);
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
	printf("%d", score_now);
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
}

void File_out();

void initsnake() {
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
	tail = (body*)malloc(sizeof(body));                    //tail为头节点
	tail->x = 24;
	tail ->y = 5;
	tail ->next = NULL;
	head = tail;                                                        //head为尾节点
	for (int i = 1; i <= 4; i++) {
		body* p = (body*)malloc(sizeof(body));
		p->x = 24 + 2 * i;
		p->y = 5;
		head->next = p;
		head = p;
	}
	head->next = NULL;
	body *temp = tail;
	while (temp) {
		gotoxy(temp->x, temp->y);
		printf("★");
		if (temp->next) {
			temp = temp->next;
		}
		else { break; }
	 } ;
};

void createfood() {
	while(1){
		foodx = rand() % 28 *2 + 2;
		foody = rand() % 28 + 1;
		int yes = 1;      //生成坐标符合条件
		body* tem = tail;
		while (tem) {
			if (foodx == tem->x && foody == tem->y) {
				yes = 0;
			};
			if (tem->next) {
				tem = tem->next;
			}
			else {
				break;
			}
		};
		if (yes == 1) {
			gotoxy(foodx, foody);
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0d);
			printf("●");
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
			break;
		}
	}
}

void snakemove() {
	body* p = head;              //p指旧头
	body* temp = (body*)malloc(sizeof(body));   //temp指新头
	temp->next = NULL;
	switch(direct) {
	case 1: temp->x = p->x;
		temp->y = p->y-1;
		break;
	case 2: temp->x = p->x-2;
		temp->y = p->y;
		break;
	case 3: temp->x = p->x;
		temp->y = p->y+1;
		break;
	case 4: temp->x = p->x+2;
		temp->y = p->y;
		break;
	}
	if (temp->x==0||temp->y==0||temp->x==58||temp->y==29){
		wrong = -1;
	}
	else {
		body* temp3 = tail;
		while (temp3) {
			if (temp->x == temp3->x && temp->y == temp3->y) {
				wrong = -1;
				break;
			}
			else if (temp3->next) {
				temp3 = temp3->next;
			}
			else {
				break;
			}
		}
	}
	if(wrong==0){
		p->next = temp;
		head = temp;
		int zhangda = 0;
		if (temp->x == foodx && temp->y == foody) {
			zhangda = 1;
			createfood();
		}
		gotoxy(temp->x, temp->y);
		printf("★");
		if (zhangda == 0) {
			gotoxy(tail->x, tail->y);
			printf(" ");
			body* temp1 = tail;
			tail = temp1->next;
			free(temp1);
		}
		else {
			zhangda = 0;
			enforcespeed += 7;
			score_now += (5 - speed / 50);
			score(score_now);
		}
	}
	else {
		lose();
	}
	
}

void keyboardControl() {
	if ((GetAsyncKeyState(87) & 0x8000 || GetAsyncKeyState(VK_UP) & 0x8000) && direct != 3) { direct = 1; }
	if ((GetAsyncKeyState(65) & 0x8000 || GetAsyncKeyState(VK_LEFT) & 0x8000) && direct != 4) { direct = 2; }
	if ((GetAsyncKeyState(83) & 0x8000 || GetAsyncKeyState(VK_DOWN) & 0x8000) && direct != 1) { direct = 3; }
	if ((GetAsyncKeyState(68) & 0x8000 || GetAsyncKeyState(VK_RIGHT) & 0x8000) && direct != 2) { direct = 4; }
	if (GetAsyncKeyState(27) & 0x8000) { lose(); }
	if (enforcespeed > 0) {
		speed = 50;
		enforcespeed--;
	}else if (GetAsyncKeyState(112) & 0x8000) { speed = 50; }
	else if (GetAsyncKeyState(113) & 0x8000) { speed = 150; }
	else { speed = 100; }
}

void lose() {
	qingping;
	body* clean = tail;
	body* temp4 = clean;
	while (clean) {
		if (clean->next) {
			temp4 = clean->next;
			free(clean);
			clean = temp4;
		}
		else {
			free(clean);
			clean = NULL;
		}
	}
	gotoxy(30, 10);
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
	printf("你失败了！最终得分是%d\n",score_now);
	if (score_now < highscore) {
		gotoxy(25, 11);
		printf("继续努力吧~  你离最高分还差: %d", highscore - score_now);
	}else if (score_now == highscore) {
		gotoxy(25, 11);
		printf("哇塞，你刚好达到了历史最高分: %d分!", highscore);
	}
	else {
		gotoxy(25, 11);
		printf("恭喜你突破了历史最高分！新的最高分: %d分！", score_now);
		fp = fopen("highscore.txt", "w+");
		fprintf(fp, "%d", score_now);
		highscore = score_now;
		fclose(fp);
	}
	gotoxy(10, 18);
	printf("5.重玩                                              2.退出游戏");
	while (1) {
		if (GetAsyncKeyState(53) & 0x8000) {
			restart =1 ;
			score_now = 0;
			break;
		}
		else if (GetAsyncKeyState(50) & 0x8000) {
			break;
		}
	}
}

void explanation() {
	qingping ;
	printf("·游戏说明\n\n一.操作说明\n使用WASD/↑←↓→控制方向\n按住F1加速，按住F2减速\n点击ESC键自动失败\n\n");
	printf("二.游戏规则\n控制贪吃蛇的行进方向，让贪吃蛇吃到食物。吃到食物时，贪吃蛇会变长并得分，不要让贪吃蛇撞到墙或身子。\n");
	printf("加速时吃食物会额外得分，减速时吃食物得分较少。\n\n\n\n");
	printf("按4返回主菜单...\n");
	while (1) {
		if (GetAsyncKeyState(52) & 0x8000) {
			qingping;
			break;
		}
	}
};

void gotoxy(int x, int y) {
	COORD pos = { (short)x,(short)y };
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(hOut, pos);
}